package com.guess.learn.java;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: ThreadPoolTest
 * @Description: 线程池的基本用法
 * @Author: tianranll <tianranll@126.com>
 * @Date: 2017/10/16 13:01
 */
public class ThreadPoolTest {

    /**
     * 1.好处
     *  1）降低资源消耗（重复利用，避免频繁创建、销毁）
     *  2）提高响应速度（当任务到达时，不需要等到线程创建就能立即执行）
     *  3）提高线程的可管理性（稀缺资源，无限制创建不但降低性能，还会降低稳定性，使用线程池可进行统一分配、调优和监控）
     */

    /**
     * 2.原理
     *  1）提交任务
     *  2）如果当前运行的线程数少于corePoolSize，则创建新线程来执行任务（需要获取全局锁）
     *  3）如果当前运行的线程数大于或等于corePoolSize，则将任务加入BlockingQueue
     *  4）如果队列已满，无法加入，则创建新的线程来处理任务（需要获取全局锁）
     *  5）如果创建新线程使当前运行的线程超出maximumPoolSize，任务将被拒绝，并调用RejectedExceptionHandler.rejectedException()方法
     */

    /**
     * 3.合理分配
     *  1）性质：CPU密集（小一点，Ncpu+1）、IO密集（大一点，Ncpu*2）、混合（如果两个相差时间不太大，拆分成CPU密集、IO密集）
     *  2）优先级：高的先执行（低的有可能永远都不会被执行）
     *  3）执行时间：可以交给不同规模的线程池来处理，或者使用优先级队列（短的先执行）
     *  4）依赖性：是否依赖其他系统资源，例如数据库（设的大一点，更好的利用CPU）
     *
     *  5）建议使用有界队列：增加系统稳定性和预警能力（比如无界队列很可能导致队列越来越大，可能会撑满内存，导致整个系统都不可用）
     */

    /**
     * 4.使用
     */

    public static void main(String[] args) throws InterruptedException {
        /**
         * 1）创建
         *  a）corePoolSize：线程池的基本大小（核心线程池），当提交一个任务时，线程池会创建一个线程来执行任务，即使其他空闲的基本线程能够执行
         *      新任务也会创建线程，等到需要执行的任务数大于线程池基本大小时就不再创建。（prestartAllCoreThreads()方法会提前创建并启动所有基本线程）
         *  b）maximumPoolSize：线程池最大数量
         *  c）keepAliveTime、TimeUnit：最大空闲时间
         *  d）BlockingQueue<Runnable> workQueue：任务队列
         *  e）ThreadFactory threadFactory：
         *  f）RejectedExecutionHandler handler：饱和策略（AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy）
         */

        /**
         * 核心线程池大小为3
         * 线程池总大小为5
         * 任务队列大小为2
         * 最大空闲时间为3秒
         */
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,5,3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2));

        /**
         * 2）提交任务
         *  a）execute
         *  b）submit
         */

        /**
         * 添加10个任务
         * 前3个直接创建新线程执行
         * 添加4、5时，核心线程池已满，此时队列为空，4、5进队列
         * 添加6、7时，核心线程池已满、队列已满，线程池未满，创建新线程开始执行6、7
         * 添加8时，队列、线程池都已满，按照饱和策略进行处理（默认抛出异常）
         * 1执行完成后从队列取出4开始执行
         * 2执行完成后从队列取出5开始执行
         * 执行顺序：1、2、3、6、7、4、5
         */

        // for (int i = 1; i <= 10; i++) {
        //     executor.execute(new MyThread("task"+i));
        //     Thread.sleep(100);
        // }

        for (int i = 1; i <= 5; i++) {
            executor.execute(new MyThread("task"+i));
            Thread.sleep(100);
        }

        // 测试最大空闲时间（上面已经创建过线程），等待30秒后线程池情况
        // TODO 貌似有问题
        System.out.println("等待30秒...");
        Thread.sleep(30000);
        System.out.println("getTaskCount()："+executor.getTaskCount());
        System.out.println("getCompletedTaskCount()："+executor.getCompletedTaskCount());
        System.out.println("getLargestPoolSize()："+executor.getLargestPoolSize());
        System.out.println("getPoolSize()："+executor.getPoolSize());
        System.out.println("getActiveCount()："+executor.getActiveCount());
        System.out.println("getCorePoolSize()："+executor.getCorePoolSize());
        System.out.println("getMaximumPoolSize()："+executor.getMaximumPoolSize());


        /**
         * 3）关闭：遍历线程池中的工作线程，然后逐个调用interrupt方法来终端线程，所以无法响应中断的任务可能永远无法终止
         *  a）shutdown：只是将线程池的状态设置成SHUTDOWN状态，然后中断所有没有正在执行任务的线程
         *  b）shutdownNow：首先将线程池的状态设置成STOP，然后尝试停止所有的正在执行或暂停任务的线程，并返回等待执行任务的列表
         * 通常使用shutdown，如果任务不一定要执行完毕，则可以调用shutdownNow
         */
        executor.shutdown();
        // executor.shutdownNow();

        /**
         * 二者调用其一，isShutdown()就会返true，当所有的任务都已关闭后，才表示线程池关闭成功，此时isTerminated()方法返回true。
         */
        System.out.println("isShutdown()："+executor.isShutdown());
        System.out.println("isTerminated()："+executor.isShutdown());

    }
}
